package com.example.demojdbcmysql.config;

import io.shardingjdbc.core.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithmType;
import io.shardingjdbc.core.api.config.MasterSlaveRuleConfiguration;
import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration;
import io.shardingjdbc.core.jdbc.core.datasource.ShardingDataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

/**
 * @Description:
 * @Author: kai.yang
 * @Date: 2019-10-24 14:08
 */
@Configuration
public class ShardingDataSourceConfiguration {


    /**
     * 主写库1
     * @return
     */
    @Bean("master1")
    @ConfigurationProperties(prefix = "spring.datasource.master1")
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 主写库2
     * @return
     */
    @Bean("master2")
    @ConfigurationProperties(prefix = "spring.datasource.master2")
    public DataSource dataSource2() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 从读库1
     * @return
     */
    @Bean("master1_1")
    @ConfigurationProperties(prefix = "spring.datasource.master11")
    public DataSource dataSource1_1() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 从读库2
     * @return
     */
    @Bean("master2_2")
    @ConfigurationProperties(prefix = "spring.datasource.master22")
    public DataSource dataSource2_2() {
        return DataSourceBuilder.create().build();
    }



    @Bean(name = "shardDataSource")
    @Primary
    public DataSource shardDataSource(@Qualifier("master1") DataSource dataSource,
        @Qualifier("master2") DataSource dataSource2,@Qualifier("master1_1") DataSource dataSource3,
        @Qualifier("master2_2") DataSource dataSource4) throws SQLException {

        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

        List<MasterSlaveRuleConfiguration> configurationList = new ArrayList<>();

        //1.读写分离配置一，master1为写库1，master1_1为读库1，这两个库将会在数据库层面进行配置主从数据同步
        MasterSlaveRuleConfiguration masterSlaveRuleConfiguration = new MasterSlaveRuleConfiguration();
        masterSlaveRuleConfiguration.setName("master1Config");
        masterSlaveRuleConfiguration.setMasterDataSourceName("master1");
        List<String> slaves = new ArrayList<>();
        slaves.add("master1_1");
        masterSlaveRuleConfiguration.setSlaveDataSourceNames(slaves);
        masterSlaveRuleConfiguration.setLoadBalanceAlgorithmType(MasterSlaveLoadBalanceAlgorithmType.ROUND_ROBIN);
        configurationList.add(masterSlaveRuleConfiguration);

        //2.读写分离配置二，master1为写库2，master2_2为读库2，这两个库将会在数据库层面进行配置主从数据同步
        MasterSlaveRuleConfiguration masterSlaveRuleConfiguration2 = new MasterSlaveRuleConfiguration();
        masterSlaveRuleConfiguration2.setName("master2Config");
        masterSlaveRuleConfiguration2.setMasterDataSourceName("master2");
        List<String> slaves2 = new ArrayList<>();
        slaves2.add("master2_2");
        masterSlaveRuleConfiguration2.setSlaveDataSourceNames(slaves2);
        masterSlaveRuleConfiguration2.setLoadBalanceAlgorithmType(MasterSlaveLoadBalanceAlgorithmType.ROUND_ROBIN);
        configurationList.add(masterSlaveRuleConfiguration2);

        //3，将读写分离配置加到主配置类中
        shardingRuleConfig.setMasterSlaveRuleConfigs(configurationList);

        // 4。将分库分表规则加入到主配置类中
        shardingRuleConfig.getTableRuleConfigs().add(getUserLogTableRuleConfiguration());

        // 打印SQL
        Properties props = new Properties();
        props.put("sql.show", true);

        Map<String, DataSource> map = new HashMap<>();
        map.put("master1", dataSource);
        map.put("master2", dataSource2);
        map.put("master1_1", dataSource3);
        map.put("master2_2", dataSource4);
        return new ShardingDataSource(shardingRuleConfig.build(map),
            new ConcurrentHashMap(), props);
    }


    /**
     * 配置用户日志读分库分表策略
     * 1。 根据user_ip的hashcode进行分库，写入不同主库
     *          目前主写库有两个：master1，master2
     * 2。根据invoke_time进行分表，写入不同的子表
     *          目前子表有：
     *          auth_user_log，默认表
     *          auth_user_log_201903，19年第三季度表
     *          auth_user_log_201904，19年第四季度表
     * @return
     */
    @Bean
    TableRuleConfiguration getUserLogTableRuleConfiguration() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();

        orderTableRuleConfig.setLogicTable("auth_user_log");
        orderTableRuleConfig.setLogicIndex("invoke_time");

        //设置数据库分库策略，根据ip的hash值，按2的倍数分库
        orderTableRuleConfig.setDatabaseShardingStrategyConfig(
            new StandardShardingStrategyConfiguration("user_ip", UserLogDbShardingAlgorithm.class.getName()));

        // 设置分表策略
        orderTableRuleConfig.setTableShardingStrategyConfig(
            new StandardShardingStrategyConfiguration("invoke_time", UserLogTableShardingAlgorithm.class.getName(),
                UserLogTableRangeShardingAlgorithm.class.getName()));

        // 设置数据节点，格式为dbxx.tablexx。这里的名称要和map的别名一致。下面两种方式都可以
        orderTableRuleConfig.setActualDataNodes(UserLogTableRangeShardingAlgorithm.toUserLogDbConfigString());

        return orderTableRuleConfig;
    }


    @Bean(name = "transactionManager")
    public DataSourceTransactionManager registerPowerTransactionManager(
        @Qualifier("shardDataSource") DataSource shardDataSource) throws Throwable {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(shardDataSource);
        return dataSourceTransactionManager;
    }

    //
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("shardDataSource") DataSource shardDataSource)
        throws Throwable {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(shardDataSource);
        //mapper
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] mapperResources = resolver.getResources("classpath:mapper/*Mapper.xml");
        sqlSessionFactoryBean.setMapperLocations(mapperResources);
        //mybatis-config
        ResourceLoader loader = new DefaultResourceLoader();
        Resource configLocation = loader.getResource("classpath:mybatis-config.xml");
        sqlSessionFactoryBean.setConfigLocation(configLocation);
        return sqlSessionFactoryBean;
    }

}
